#include "ckernel.h"
#include <QDebug>
#include"TcpClientMediator.h"
#include<QMessageBox>
#include"useritem.h"
#include<QTextCodec>
#include<QInputDialog>
#include<QTime>
#include<QFile>
#include<windows.h>
#include<winsock2.h>
//计算协议表数组下标的宏
#define NetProtocalFunMap(a) m_netProtocalMap[a - _DEF_PROTOCAL_BASE - 10]

//初始化协议映射表
void CKernel::setProtocalMap()
{
    memset(m_netProtocalMap,0,sizeof(m_netProtocalMap));
    //绑定协议头和处理函数
    NetProtocalFunMap(_DEF_TCP_REGISTER_RS) = &dealRegisterRs;

    NetProtocalFunMap(_DEF_TCP_LOGIN_RS) = &dealLoginRs;

    NetProtocalFunMap(_DEF_TCP_FRIEND_INFO) = &dealFriendInfoRq;

    NetProtocalFunMap(_DEF_TCP_CHAT_RQ) = &dealChatRq;

    NetProtocalFunMap(_DEF_TCP_CHAT_RS) = &dealChatRs;

    NetProtocalFunMap(_DEF_TCP_ADD_FRIEND_RQ)=&dealAddFriendRq;

    NetProtocalFunMap(_DEF_TCP_ADD_FRIEND_RS)=&dealAddFriendRs;

    NetProtocalFunMap(_DEF_TCP_OFFLINE)=&dealOfflineRq;

    NetProtocalFunMap(_DEF_PROTOCAL_FILE_INFO_RQ)=&dealSendFileRq;

    NetProtocalFunMap(_DEF_PROTOCAL_FILE_INFO_RS)=&dealSendFileRs;

    NetProtocalFunMap(_DEF_PROTOCAL_FILE_BLOCK_RQ)=&dealFileBlockRq;

    NetProtocalFunMap(_DEF_TCP_ADD_GROUP_RS)=&dealAddGroupRs;

    NetProtocalFunMap(_DEF_SEND_GROUP_MSG_RQ)=&dealGroupMesRq;

    NetProtocalFunMap(_DEF_TCP_GROUP_INFO)=&dealGroupInfoRq;

    NetProtocalFunMap(_DEF_GROUP_INVITE_RQ)=&dealGroupInviteRq;

    NetProtocalFunMap(_DEF_GROUP_INVITE_RS)=&dealGroupInviteRs;

    NetProtocalFunMap(_DEF_DEL_FRIEND_RQ)=&dealDelFriendRq;

    NetProtocalFunMap(_DEF_GROUP_LIST_INFO)=&dealGroupListInfo;

}
CKernel::CKernel(QObject *parent) : QObject(parent),m_id(0)
{
    //初始化协议映射表
    setProtocalMap();
    //显示登录&注册页面
    m_LoginDialog = new LoginDialog;
    m_LoginDialog->showNormal();
    m_LoginDialog->setWindowTitle("MyChat");
    //绑定发送注册信息的信号和槽函数
    connect(m_LoginDialog,SIGNAL(SIG_registerCommit(QString,QString,QString)),
            this, SLOT(slot_registerCommit(QString,QString,QString)));
    //绑定发送登录信息信号和槽函数
    connect(m_LoginDialog,SIGNAL(SIG_loginCommit(QString,QString)),
            this, SLOT(slot_loginCommit(QString,QString)));
    //绑定关闭注册&登录窗口的信号和槽函数
    connect(m_LoginDialog,SIGNAL(SIG_close()),
            this, SLOT(slot_close()));
    //初始化主窗口
    m_MainWnd = new MyChatDialog;
    //m_MainWnd->show();
    //绑定添加好友的信号和槽函数
    connect(m_MainWnd,SIGNAL(SIG_addFriend()),
            this, SLOT(slot_addFriend()));
    //绑定关闭好友列表窗口的信号和槽函数
    connect(m_MainWnd,SIGNAL(SIG_close()),
            this, SLOT(slot_offline()));
    //绑定添加群聊的信号和槽函数
    connect(m_MainWnd,SIGNAL(SIG_addGroup()),
            this,SLOT(slot_addGroup()));
    //初始化中介者类对象
    m_pMediator = new TcpClientMediator;
    //打开网络
    if(!m_pMediator->OpenNet()){
        QMessageBox::about(m_MainWnd,"提示","打开网络失败");
        //退出
        exit(0);
    }
    //绑定中介者类发送来的总数据 和 处理数据槽函数
    connect(m_pMediator,SIGNAL(SIG_ReadyData(long,char*,int)),
            this,SLOT(slot_readyData(long,char*,int)));

}

CKernel::~CKernel()
{
    //回收资源
    //1、回收中介者
    if(m_pMediator){
        //关闭网络
        m_pMediator->CloseNet();
        delete m_pMediator;
        m_pMediator = nullptr;
    }
    //2、回收主窗口
    if(m_MainWnd){
        m_MainWnd->hide();
        delete m_MainWnd;
        m_MainWnd = nullptr;
    }
    //3、回收登录&注册界面
    if(m_LoginDialog){
        m_LoginDialog->hide();
        delete m_LoginDialog;
        m_LoginDialog = nullptr;
    }

}
//QString utf-8 -> char*  gb2312
void CKernel::utf8ToGb2312(QString &utf8, char *gbBuf, int nLen)
{
    //转码对象
    QTextCodec* gb2312code = QTextCodec::codecForName("gb2312");
    //QByteArray 是char类型数组的封装类
    QByteArray ba =gb2312code->fromUnicode(utf8);
    strcpy_s(gbBuf,nLen,ba.data());
}
//char*  gb2312 -> QString utf-8
QString CKernel::gb2312ToUtf8(char *gbBuf)
{
    //转码对象
   QTextCodec* gb2312code = QTextCodec::codecForName("gb2312");
   return gb2312code->toUnicode(gbBuf);
}
//接受总数据的处理槽函数
void CKernel::slot_readyData(long ISendIp, char *buf, int nLen)
{
    qDebug() << __func__;
    //1、取出协议头
    PackType nType = *(PackType*)buf;
    //根据协议头，判断走哪个处理流程
    //2、判断协议头是否在有效范围内
    if(nType >= _DEF_PROTOCAL_BASE + 10 && nType <= _DEF_PROTOCAL_BASE + 10 + _DEF_PROTOCAL_COUNT){
        //3、计算数组下标，获取对应的处理函数指针
        PFUN pFun = NetProtocalFunMap(nType);
        //4、如果指针存在，调用指针指向的处理函数
        if(pFun){
            (this->*pFun)(ISendIp,buf,nLen);
        }
    }
    //5、回收buf
    delete []buf;
}
//处理注册信息的槽函数
void CKernel::slot_registerCommit(QString name, QString tel, QString password)
{
    qDebug() << __func__;
    //1、类型转换，QString ->  std::string
    //std::string nameStr = name.toStdString();
    std::string telStr = tel.toStdString();
    std::string passwordStr = password.toStdString();
    //2、封包
    STRU_TCP_REGISTER_RQ rq;
    utf8ToGb2312(name, rq.name,sizeof(rq.name));
    strcpy(rq.tel,telStr.c_str());
    strcpy(rq.password,passwordStr.c_str());

    //3、发送注册请求给服务器
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
}
//处理登录信息槽函数
void CKernel::slot_loginCommit(QString tel, QString password)
{
    qDebug() << __func__;
    //1、类型转换，QString ->  std::string
    std::string telStr = tel.toStdString();
    std::string passwordStr = password.toStdString();
    //2、封包
    STRU_TCP_LOGIN_RQ rq;
    strcpy(rq.tel,telStr.c_str());
    strcpy(rq.password,passwordStr.c_str());

    //3、发送注册请求给服务器
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
}
//处理点击用户头像槽函数
void CKernel::slot_userItemClicked(int id)
{
    //判断与该好友的聊天窗口是否存在，存在就显示出来
    if(m_mapIdToChatdialog.find(id)!=m_mapIdToChatdialog.end()){
        ChatDialog*  chat = m_mapIdToChatdialog[id];
        chat->showNormal();
    }
}
//处理点击群聊头像槽函数
void CKernel::slot_groupItemClicked(int id)
{
    //判断与该好友的聊天窗口是否存在，存在就显示出来
    if(m_mapIdToGroupdialog.find(id)!=m_mapIdToGroupdialog.end()){
        Groupdialog*  chat = m_mapIdToGroupdialog[id];
        chat->showNormal();
    }
}
//处理用户发送聊天内容的槽函数
void CKernel::slot_sendChatMsg(int id, QString content)
{
    qDebug() << __func__;
    //1、封包
    STRU_TCP_CHAT_RQ rq;
    //2、聊天内容不转换编码，因为在数据库和服务器都没用到
    rq.userId = m_id;
    rq.friendId = id;
    strcpy(rq.content,content.toStdString().c_str());
    //3、发送
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));//底层已经保存了
}
//添加好友的处理槽函数
void CKernel::slot_addFriend()
{
    qDebug()<<__func__;
   //1、弹出一个添加好友的窗口
  QString strName = QInputDialog::getText(m_MainWnd,"添加好友","请输入好友昵称");
  //2、校验用户输入的昵称是否合法
  if(strName.isEmpty()||strName.length()>10){
      QMessageBox::about(m_MainWnd,"提示","输入昵称不合法");
      return;
  }
  //3、判断是不是自己，如果是自己，不能添加
  if(strName == m_name){
      QMessageBox::about(m_MainWnd,"提示","不能添加自己为好友");
      return;
  }
  //4、判断是不是已经是好友了，如果是好友了就不需要添加
  for(auto ite = m_mapIdToUserItem.begin();ite!=m_mapIdToUserItem.end();ite++){
      UserItem* item = *ite;
      if(item->m_name==strName){
           QMessageBox::about(m_MainWnd,"提示","该用户已经是你的好友了，不能添加为好友");
           return;
      }
  }
  //5、封包
  STRU_TCP_ADD_FRIEND_RQ rq;   //为什么不是指针*？ 如果new对象的话还得回收,如果定义一个临时的（不加*）走到大括号系统就自动回收了
  rq.userId = m_id;
  //A添加B为好友，A的昵称只在用户B客户端显示，不需要在服务端中使用，所以不需要转码
  strcpy(rq.userName,m_name.toStdString().c_str());
  strcpy(rq.friendName,strName.toStdString().c_str());

  //6、发给服务端
  m_pMediator->SendData(0,(char*)&rq,sizeof(rq));

}
//添加群聊的处理槽函数
void CKernel::slot_addGroup()
{
    qDebug()<<__func__;
    //1、弹出一个添加群聊的窗口
    QString strName = QInputDialog::getText(m_MainWnd,"创建群聊","请输入群聊昵称");
    //2、校验用户输入的昵称是否合法
    if(strName.isEmpty()||strName.length()>10){
      QMessageBox::about(m_MainWnd,"提示","输入昵称不合法");
      return;
    }
    //3、封包
    STRU_TCP_ADD_GROUP_RQ rq;   //为什么不是指针*？ 如果new对象的话还得回收,如果定义一个临时的（不加*）走到大括号系统就自动回收了
    rq.userId = m_id;
    strcpy(rq.userName,m_name.toStdString().c_str());
    strcpy(rq.groupName,strName.toStdString().c_str());
    //6、发给服务端
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
}
//发送群消息的处理槽函数
void CKernel::slot_sendGroupMsg(int id, QString content)
{
    qDebug()<<__func__;
    //1、封包
    STRU_GROUP_MSG_RQ rq;
    //2、赋值
    //聊天内容不需要编码转换，因为聊天内容在服务器和数据库都不使用
    rq.userId = m_id;
    rq.groupId = id;
    strcpy(rq.name,m_name.toStdString().c_str());
    strcpy(rq.content,content.toStdString().c_str());
    //3、发送
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));//底层已经保存了
}
//处理关闭登录&注册窗口的信号的槽函数
void CKernel::slot_close()
{
    qDebug() << __func__;
    //1、回收资源
     //1.1、好友列表界面对象
     if(m_MainWnd)
     {
        m_MainWnd->hide();
        delete m_MainWnd;
        m_MainWnd=NULL;
     }
     //1.2、登录&注册界面对象
     if(m_LoginDialog){
         m_LoginDialog->hide();
         delete m_LoginDialog;
         m_LoginDialog=NULL;
     }
     //管理id->聊天窗口
     for(auto ite = m_mapIdToChatdialog.begin();ite!=m_mapIdToChatdialog.end();){
         ChatDialog* chat=*ite;
         if(chat)
         {
             chat->hide();
             delete chat;
             chat=NULL;
         }
         ite = m_mapIdToChatdialog.erase(ite);
     }

     //1.4、中介者类对象
     //INetMediator* m_pMediator;
     if(m_pMediator)
     {
         m_pMediator->CloseNet();
         delete m_pMediator;
         m_pMediator=NULL;
     }
     //2、退出进程
     exit(0);
}
//关闭好友列表窗口的槽函数
void CKernel::slot_offline()
{
    qDebug()<<__func__;
    //1、发送下线请求
    STRU_TCP_OFFLINE_RQ rq;
    rq.userId = m_id;
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
    //2、回收资源和退出进程
    slot_close();
}


//处理添加好友请求
void CKernel::dealAddFriendRq(long ISendIp, char *buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_TCP_ADD_FRIEND_RQ* rq = (STRU_TCP_ADD_FRIEND_RQ*)buf;
    //2、定义添加好友回复包
    STRU_TCP_ADD_FRIEND_RS rs;
    rs.friendId =m_id;
    rs.userId= rq->userId;
    strcpy(rs.friendName,m_name.toStdString().c_str());
    //3、弹窗询问用户是否同意添加好友
    QString str =QString("用户[%1]请求添加你为好友，是否同意？").arg(rq->userName);
    if(QMessageBox::Yes==QMessageBox::question(m_MainWnd,"添加好友",str))
    {
        //同意添加好友
        rs.result= add_success;
    }else{
        //拒绝添加好友
        rs.result=user_refuse;
    }
    //4、发送添加好友回复包给服务器
    m_pMediator->SendData(0,(char*)&rs,sizeof(rs));
}
//处理添加好友回复
void CKernel::dealAddFriendRs(long ISendIp, char *buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_TCP_ADD_FRIEND_RS* rs=(STRU_TCP_ADD_FRIEND_RS*)buf;
    //2、好友昵称需要转码
    QString strName =gb2312ToUtf8(rs->friendName);

    //3、取出结果显示信息
    switch (rs->result) {
    case add_fail:
      QMessageBox::about(m_MainWnd,"提示","添加好友失败");
      break;
    case user_offline:
      QMessageBox::about(m_MainWnd,"提示",QString("用户[%1]不在线，添加好友失败").arg(strName));
      break;
    case user_refuse:
      QMessageBox::about(m_MainWnd,"提示",QString("用户[%1]拒绝添加好友").arg(rs->friendName));
      break;
    case no_this_exist:
      QMessageBox::about(m_MainWnd,"提示",QString("用户[%1]不存在，添加好友失败").arg(strName));
      break;
    case add_success:
      QMessageBox::about(m_MainWnd,"提示",QString("添加好友[%1]成功").arg(rs->friendName));
      break;
    }
}
//处理添加群聊回复
void CKernel::dealAddGroupRs(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //1.拆包
    STRU_TCP_ADD_GROUP_RS* rs = (STRU_TCP_ADD_GROUP_RS*)buf;
    //2.弹出建群结果
    switch (rs->result) {
    case add_fail:
      QMessageBox::about(m_MainWnd,"提示","添加群聊失败，群聊已存在");
      break;
    case add_success:
      QMessageBox::about(m_MainWnd,"提示","添加群聊成功");
      break;
    }
}
//处理群聊的属性请求
void CKernel::dealGroupInfoRq(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //拆包
    STRU_GROUP_INFO* info = (STRU_GROUP_INFO*)buf;
    //1.1、群聊名称编码转换
    QString groupNameTmp=info->groupName;
    //遍历群聊列表
    qDebug()<<info->id<<"--";
    if(m_mapIdToGroupItem.find(info->id)==m_mapIdToGroupItem.end())
    {
        //没有new一个
        UserItem* item=new UserItem;
        item->m_type = 1;
        item->slot_setInfo(info->id,info->groupName,1,4,"");
        connect(item,SIGNAL(SIG_groupItemClicked(int)),
                this,SLOT(slot_groupItemClicked(int)));

        m_MainWnd->slot_addGroup(item);

        Groupdialog* chat=new Groupdialog;
        chat->slot_setInfo(groupNameTmp,info->id);

        connect(chat,SIGNAL(SIG_sendChatMsg(int,QString)),
                this,SLOT(slot_sendGroupMsg(int,QString)));
        connect(chat,SIGNAL(SIG_groupInvite(int,QString)),
                this,SLOT(slot_groupInvite(int,QString)));
        connect(chat,SIGNAL(SIG_exitGroup(int)),
                this,SLOT(slot_exitGroup(int)));
        connect(chat,SIGNAL(SIG_groupList(int)),
                this,SLOT(slot_groupList(int)));
        //TODO:6.聊天窗口放到map中管理
        m_mapIdToGroupdialog[info->id]=chat;
        //放在map中管理
        m_mapIdToGroupItem[info->id]= item;
    }
}
//处理下线请求
void CKernel::dealOfflineRq(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //1、拆包
    STRU_TCP_OFFLINE_RQ* rq=(STRU_TCP_OFFLINE_RQ*)buf;
    //2、判断m_mapIdToUserItem中是否有这个下线用户
    if(m_mapIdToUserItem.count(rq->userId)>0)
    {
        //3、如果存在，取出下线用户的useritem,设置下线状态
        UserItem* item = m_mapIdToUserItem[rq->userId];
        item->slot_setUserOffline();
    }
}
//处理发送文件请求
void CKernel::dealSendFileRq(long ISendIp, char *buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_FILE_INFO_RQ * rq = (STRU_FILE_INFO_RQ*)buf;
    STRU_FILE_INFO_RS rs;
    QString ss = gb2312ToUtf8(rq->szFileName);
    //2、弹出提示窗口，询问是否同意接收文件
    QString str = QString("你的好友请求向你发送文件[%1]，是否同意？").arg(ss);
//    char str[1024] = "";
//    sprintf(str,"你的好友请求向你发送文件[%s]，是否同意？",rq->szFileName);
//    QString ss = gb2312ToUtf8(str);
    if(QMessageBox::Yes == QMessageBox::question(m_MainWnd,"提示",str))
    {
      rs.nRes = _file_accept;

      //3、打开保存浏览窗口 选择保存路径
      char FILE_PATH[1024] = "";
      OPENFILENAMEA file = {0};
      file.lStructSize = sizeof(file);
      file.lpstrFilter = "AllFile(*.*)\0*.*\0";          //过滤器
      file.lpstrFile = FILE_PATH; //open文件的路径存储
      file.nMaxFile = sizeof(FILE_PATH) / sizeof(*FILE_PATH);  //缓冲区能存放的字符数
      file.Flags = OFN_EXPLORER;
      strcpy_s(file.lpstrFile, 1024, rq->szFileName);

      BOOL flag = GetSaveFileNameA(&file);    //默认接收
      if(FALSE == flag)
      {
          strcpy_s(file.lpstrFile, 1024, rq->szFileName);   //相对于exe同级的
      }

      //创建一个新的文件结构体并赋值，为文件的接收做准备
      FileInfo* info = new FileInfo;
      info->nFileSize = rq->szFileSize;
      info->nPos = 0;

      strcpy(info->szFileID, rq->szFileID);
      strcpy(info->szFileName, rq->szFileName);
      strcpy(info->szFilePath, file.lpstrFile);//新另存的文件名

      fopen_s(&info->pFile, info->szFilePath, "wb");    //打开文件成功返回0，将文件写入info->pFile中

      qDebug() << info->szFileID;
      qDebug() << info->szFileName;
      qDebug() << info->szFilePath;
      qDebug() << info->nFileSize;

      //存储 文件信息，方便接收文件块
      QString strID = info->szFileID;
      if(m_mapIdToFileInfo.count(strID)==0)
      {
          m_mapIdToFileInfo[strID] = info;
          qDebug() << "dealSendFileRq: FileInfo存储完毕...";
      }
    }
    else
    {
      rs.nRes = _file_refuse;
    }
    rs.friendid = rq->userid;
    rs.userid = m_id;
    rs.szFileSize = rq->szFileSize;
    strcpy(rs.szFileID, rq->szFileID);
    strcpy(rs.szFilePath, rq->szFilePath);

    m_pMediator->SendData(0, (char*)&rs, sizeof(rs));
}
//处理注册回复
void CKernel::dealRegisterRs(long ISendIp, char* buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_TCP_REGISTER_RS* rs = (STRU_TCP_REGISTER_RS*)buf;

    //2、取出注册结果，提示用户
    switch (rs->result) {
    case user_is_exist:
        QMessageBox::about(m_LoginDialog,"提示","注册失败，用户已存在");
        break;
    case parameter_error:
        QMessageBox::about(m_LoginDialog,"提示","注册信息不符合要求");
        break;
    case register_fail:
        QMessageBox::about(m_LoginDialog,"提示","注册失败，数据库更新失败");
        break;
    case register_success:
        QMessageBox::about(m_LoginDialog,"提示","注册成功");
        break;
    }
}
//处理登录回复
void CKernel::dealLoginRs(long ISendIp, char* buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_TCP_LOGIN_RS* rs = (STRU_TCP_LOGIN_RS*)buf;
    //2、取出登录结果，提示用户
    switch (rs->result) {
    case password_error:
        QMessageBox::about(m_LoginDialog,"提示","登录失败，密码错误");
        break;
    case user_not_exist:
        QMessageBox::about(m_LoginDialog,"提示","登录失败，用户不存在");
        break;
    case login_success:
        //QMessageBox::about(m_LoginDialog,"提示","登录成功");
        {
            //隐藏登录界面，显示好友列表界面
            m_LoginDialog->hide();
            m_MainWnd->showNormal();
            //保存用户id
            m_id = rs->userId;
        }
        break;
    }
}
//处理好友信息请求
void CKernel::dealFriendInfoRq(long ISendIp, char *buf, int nLen)
{
    qDebug() << __func__;
    //1、拆包
    STRU_TCP_FRIEND_INFO* info = (STRU_TCP_FRIEND_INFO*)buf;
    //1.1昵称和签名编码转换
    //QString nameTemp = gb2312ToUtf8(info->name);
    //QString feelingTemp = gb2312ToUtf8(info->feeling);
    QString nameTemp =info->name;
    QString feelingTemp=info->feeling;
    //2、判断是不是自己的信息
    if(m_id == info->userId){
        //2.1、如果是自己的信息，更新用户界面，然后返回
        //保存自己的name
        m_name = nameTemp;
        m_MainWnd->slot_setInfo(nameTemp,feelingTemp,info->iconId);
        return;
    }
    //3、查看好友列表里是否有这个好友了
    if(m_mapIdToUserItem.find(info->userId) == m_mapIdToUserItem.end()){
        //4、没找到 如果没有这个好友，就new一个item
        UserItem* item = new UserItem;
        item->m_type = 0;
        item->slot_setInfo(info->userId,nameTemp,info->state,info->iconId,feelingTemp);
        //4.1绑定用户点击头像信号和槽函数
        connect(item,SIGNAL(SIG_userItemClicked(int)),
                this,SLOT(slot_userItemClicked(int)));
        //4.2创建聊天窗口
        ChatDialog* chat = new ChatDialog;
        chat->sentInfo(nameTemp,info->userId);
        //4.3绑定聊天窗口发送信息的槽函数
        connect(chat,SIGNAL(SIG_sendChatMsg(int,QString)),
                this,SLOT(slot_sendChatMsg(int,QString)));
        //绑定聊天窗口发送文件的槽函数
        connect(chat,SIGNAL(SIG_sendFile(int)),
                this,SLOT(slot_sendFileMsg(int)));
        //绑定聊天窗口删除好友的槽函数
        connect(chat,SIGNAL(SIG_DelFriend(int)),
                this,SLOT(slot_delFriend(int)));
        //4.4聊天窗口放到map中管理
        m_mapIdToChatdialog[info->userId] = chat;
        //4.5添加到好友列表
        m_MainWnd->slot_addFriend(item);
        //4.6添加到map中管理
        m_mapIdToUserItem[info->userId] = item;

    }else{
        //5、如果有就更新好友列表
        //5.1、取出UserItem
        UserItem* item = m_mapIdToUserItem[info->userId];
        //5.2、如果好友之前是下线状态，现在是上线状态
        if(item && item->m_status == 0 && info->state == 1){
            QString up = QString("%1上线啦！").arg(nameTemp);
            QMessageBox::about(m_MainWnd,"提示",up);
        }
        //5.3、更新好友信息
        item->slot_setInfo(info->userId,nameTemp,info->state,info->iconId,feelingTemp);
    }


}
//处理服务端发过来的聊天请求
void CKernel::dealChatRq(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //1、拆包
    STRU_TCP_CHAT_RQ* rq =(STRU_TCP_CHAT_RQ*)buf;
    //2、查看该该用户的聊天窗口是否存在
    if(m_mapIdToChatdialog.find(rq->userId)!=m_mapIdToChatdialog.end())
    {
        //3、聊天窗口存在，先设置用户不在线信息，并且显示出来
        ChatDialog* chat=m_mapIdToChatdialog[rq->userId];
        chat->slot_setChatMsg(rq->content);
        chat->showNormal();
    }
}
//处理服务器发过来的群聊内容请求
void CKernel::dealGroupMesRq(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //1、拆包
    STRU_GROUP_MSG_RQ* rq =(STRU_GROUP_MSG_RQ*)buf;
    //2、查看该该用户的聊天窗口是否存在
    qDebug()<<rq->groupId;
    if(m_mapIdToGroupdialog.find(rq->groupId)!=m_mapIdToGroupdialog.end())
    {
        //3、聊天窗口存在显示出来
        Groupdialog* chat=m_mapIdToGroupdialog[rq->groupId];
        QString name = rq->name;
        chat->slot_setChatMssage(rq->content,name);
        chat->showNormal();
    }
}
//处理服务端发过来的聊天回复
void CKernel::dealChatRs(long ISendIp, char *buf, int nLen)
{
    qDebug()<<__func__;
    //1、拆包
    STRU_TCP_CHAT_RS* rs =(STRU_TCP_CHAT_RS*)buf;
    //2、查看该该用户的聊天窗口是否存在
    if(m_mapIdToChatdialog.find(rs->friendId)!=m_mapIdToChatdialog.end())
    {
        //3、聊天窗口存在，先设置用户不在线信息，并且显示出来
        ChatDialog* chat=m_mapIdToChatdialog[rs->friendId];
        chat->slot_setUserOffline();
        chat->showNormal();
    }
}
//获取文件名
char * GetFileName(char *path)
{
    if(!path)   return NULL;
    int nLen = strlen(path);
    if(nLen < 1)
    {
         return NULL;
    }
    qDebug()<<path;
    //裁剪
    for(int i = nLen-1; i >= 0; i--)
    {
        if(path[i] == '\\' || path[i] == '/')
        {
            return &path[i+1];
        }

    }
}
//发送文件信息
void CKernel::slot_sendFileMsg(int id)
{
    qDebug() << __func__;

    //封装数据包，然后发送给服务端
    STRU_FILE_INFO_RQ rq;
    rq.userid = m_id;   //发送方的id
    rq.friendid = id;   //接收方的id

    //-----------------------

    char FILE_PATH[1024] = "";
    OPENFILENAMEA file = {0};
    file.lStructSize = sizeof(file);
    file.lpstrFilter = "txt(*.txt)\0*.txt\0All(*.*)\0*.*\0";          //过滤器
    file.lpstrFile = FILE_PATH; //open文件的路径存储
    file.nMaxFile = sizeof(FILE_PATH) / sizeof(*FILE_PATH);  //缓冲区能存放的字符数
    file.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

    BOOL flag = GetOpenFileNameA(&file);    //默认接收
    if(FALSE == flag)
    {
        return;
    }
    qDebug() << file.lpstrFile;

    //-----------------------------

    //保存文件名字
    //QString strFileName = gb2312ToUtf8(GetFileName(file.lpstrFile));
    char * strFileName = GetFileName(file.lpstrFile);
    strcpy_s(rq.szFileName, _MAX_FILE_PATH, strFileName);    //发送文件的名字（绝对路径）
    qDebug() << rq.szFileName;

    //获取系统时间、保存文件id
    QString FileIdTemp = QString("%1_%2").arg(file.lpstrFile).arg(QTime::currentTime().toString("hh:mm:ss"));
    strcpy(rq.szFileID, FileIdTemp.toStdString().c_str());    //发送文件的标识符id = 文件名_当前时间
    qDebug() << rq.szFileID;


    QString FilePathTemp = QString("%1").arg(file.lpstrFile);
    strcpy(rq.szFilePath, FilePathTemp.toStdString().c_str());


    //获取文件大小
    FILE * pFile = NULL;
    fopen_s(&pFile, file.lpstrFile, "rb");
    fseek(pFile, 0, SEEK_END);  //跳到文件尾
    rq.szFileSize = _ftelli64(pFile);
    fseek(pFile, 0, SEEK_SET);  //跳到文件头
    qDebug() << rq.szFileSize;

    fclose(pFile);

    //先发送文件信息
    m_pMediator->SendData(0, (char*)&rq, sizeof(rq));
}
//处理群聊邀请成员信号的槽函数
void CKernel::slot_groupInvite(int id,QString groupname)
{
    qDebug() << __func__;
    //1、弹出一个添加群聊的窗口
    QString strName = QInputDialog::getText(m_MainWnd,"邀请好友","请输入好友昵称");
    //2、校验用户输入的昵称是否合法
    if(strName.isEmpty()||strName.length()>10){
      QMessageBox::about(m_MainWnd,"提示","输入昵称不合法");
      return;
    }
    //3、封包
    STRU_GROUP_INVITE_RQ rq;   //为什么不是指针*？ 如果new对象的话还得回收,如果定义一个临时的（不加*）走到大括号系统就自动回收了
    rq.groupId = id;
    strcpy(rq.userName,strName.toStdString().c_str());
    strcpy(rq.groupName,groupname.toStdString().c_str());
    //6、发给服务端
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));

}
//处理点击退出群聊信号的槽函数
void CKernel::slot_exitGroup(int groupId)
{
    qDebug() << __func__;
    //弹出弹窗是否确定
    if(QMessageBox::Yes==QMessageBox::question(m_mapIdToGroupdialog[groupId],"退出提示","是否要退出群聊？")){
        //封包
        STRU_EXIT_GROUP_RQ rq;
        rq.groupId = groupId;
        rq.userId = m_id;

        //回收groupdialog和item
        if(m_mapIdToGroupdialog[groupId]){
            m_mapIdToGroupdialog[groupId]->hide();
            delete m_mapIdToGroupdialog[groupId];
            m_mapIdToGroupdialog[groupId] = nullptr;
            m_mapIdToGroupdialog.remove(groupId);
        }
        if(m_mapIdToGroupItem[groupId]){
            m_MainWnd->removeGroupItem(m_mapIdToGroupItem[groupId]);
            delete m_mapIdToGroupItem[groupId];
            m_mapIdToGroupItem[groupId] = nullptr;
            m_mapIdToGroupItem.remove(groupId);
        }
        //发送给服务器
        m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
    }
    else{
        //忽略
        return;
    }

}
//处理点击删除好友信号的槽函数
void CKernel::slot_delFriend(int friendId)
{
    qDebug() << __func__;
    //弹出弹窗是否确定
    if(QMessageBox::Yes==QMessageBox::question(m_mapIdToChatdialog[friendId],"退出提示","是否要删除该好友？")){
        //封包
        STRU_DEL_FRIEND_RQ rq;
        rq.m_id = m_id;
        rq.friendId = friendId;
        //回收dialog和item
        if(m_mapIdToChatdialog[friendId]){
            ChatDialog* temp = nullptr;
            temp = m_mapIdToChatdialog[friendId];
            m_mapIdToChatdialog[friendId]->hide();
            m_mapIdToChatdialog.remove(friendId);
            delete temp;
            temp = nullptr;

        }
        if(m_mapIdToUserItem[friendId]){
            UserItem *temp = nullptr;
            temp = m_mapIdToUserItem[friendId];
            m_MainWnd->removeGroupItem(m_mapIdToUserItem[friendId]);
            m_mapIdToUserItem.remove(friendId);
            delete temp;
            temp = nullptr;
        }
        //发送给服务器
        m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
    }
    else{
        //忽略
        return;
    }
}
//处理点击刷新按钮的槽函数
void CKernel::slot_groupList(int groupId)
{
    qDebug() << __func__;
    //封包
    STRU_GROUP_LIST_RQ rq;
    rq.groupId = groupId;
    rq.userId = m_id;
    //将列表清空
    m_mapIdToGroupdialog[groupId]->clearList();
    //发送刷新群组列表的请求
    m_pMediator->SendData(0,(char*)&rq,sizeof(rq));
}
//处理发送文件回复
void CKernel::dealSendFileRs(long lSendIP, char *buf, int nLen)
{

    qDebug() << __func__;

    //1、拆包
    STRU_FILE_INFO_RS * rs = (STRU_FILE_INFO_RS*)buf;


    if(rs->nRes == _file_accept)
    {
        //2、如果好友愿意接收，就继续发送文件块

        STRU_FILE_BLOCK_RQ blockRq;

        blockRq.userid = m_id;   //发送方的id
        blockRq.friendid = rs->userid;   //接收方的id
        long long nPos = 0;
        int nReadLen = 0;

        FILE * pFile = NULL;
        fopen_s(&pFile, rs->szFilePath , "rb");

        while(true)
        {
            //读文件发送
            //读取函数：从文件描述符pFile中读取数据，存放到FileContent之中，一次读取1个字节，读取CONTENT次
            nReadLen = fread(blockRq.szFileContent, 1, _DEF_FILE_CONTENT_SIZE, pFile);
            blockRq.nBlockSize = nReadLen;
            strcpy(blockRq.szFileID, rs->szFileID);

            m_pMediator->SendData(0, (char*)&blockRq, sizeof(blockRq));

            nPos += nReadLen;

            qDebug() << "已发送数据：" << nPos;

            if(nPos >= rs->szFileSize || nReadLen < _DEF_FILE_CONTENT_SIZE)
            {
                //关闭文件指针
                fclose(pFile);
                qDebug() << "发送端文件指针已关闭......";
                break;
            }
        }
    }

    //好友拒接接收
    else if(rs->nRes == _file_refuse)
    {
        QMessageBox::question(m_MainWnd, "提示", "好友拒绝接收文件");
        return;
    }
}

void CKernel::dealFileBlockRq(long lSendIP, char *buf, int nLen)
{
    qDebug() << __func__;

    //1、拆包
    STRU_FILE_BLOCK_RQ * rq = (STRU_FILE_BLOCK_RQ*)buf;

    //2、根据文件id找到文件信息，找不到直接返回
    if(m_mapIdToFileInfo.count(rq->szFileID) == 0)
    {
        qDebug() << "DealBlockRq返回: 根据rq->FileId,没找到FileInfo.... ";
        return;
    }

    FileInfo * pInfo = m_mapIdToFileInfo[rq->szFileID];

    //写函数：从FileContent中获取数据，写入到pInfo->pFile文件中
    int nRes = fwrite(rq->szFileContent, 1, rq->nBlockSize, pInfo->pFile);
    pInfo->nPos += nRes;

    qDebug() << pInfo->nPos;

    //写入的结束条件
    if(pInfo->nPos >= pInfo->nFileSize)
    {
        //关闭文件指针
        fclose(pInfo->pFile);
        qDebug() << "写入的文件指针已关闭";

        //从map节点中去除
        auto it = m_mapIdToFileInfo.find(rq->szFileID);
        m_mapIdToFileInfo.erase(it);

        delete pInfo;
        pInfo = nullptr;
    }
}
//处理群聊邀请成员请求
void CKernel::dealGroupInviteRq(long lSendIP, char *buf, int nLen)
{
    qDebug() << __func__;
    //拆包
    STRU_GROUP_INVITE_RQ *rq = (STRU_GROUP_INVITE_RQ *)buf;
    //弹窗
    QString invite = QString("邀请你加入了群聊%1！").arg(rq->groupName);
    qDebug() <<invite;
    QMessageBox::about(m_MainWnd,"提示",invite);
}
//处理群聊邀请成员回复
void CKernel::dealGroupInviteRs(long lSendIP, char *buf, int nLen)
{
    qDebug() << __func__;
    //拆包
    STRU_GROUP_INVITE_RS* rs  = (STRU_GROUP_INVITE_RS*)buf;
    switch (rs->result) {
    case add_fail:
        QMessageBox::about(m_mapIdToGroupdialog[rs->groupid],"提示","邀请失败，当前好友已经在群聊中了");
        break;
    case add_success:
        QMessageBox::about(m_mapIdToGroupdialog[rs->groupid],"提示","邀请成功");
        break;
    }
}
//处理删除好友请求
void CKernel::dealDelFriendRq(long lSendIP, char *buf, int nLen)
{
    qDebug() << __func__;
    //拆包
    STRU_DEL_FRIEND_RQ* rq = (STRU_DEL_FRIEND_RQ*)buf;
    //回收对应的chatdialog 和 item
    //这里是被删除的人接受的 所以要用主动删除方的id

    if(m_mapIdToChatdialog[rq->m_id]){
        ChatDialog* temp = nullptr;
        temp = m_mapIdToChatdialog[rq->m_id];
        m_mapIdToChatdialog[rq->m_id]->hide();
        m_mapIdToChatdialog.remove(rq->m_id);
        delete temp;
        temp = nullptr;

    }
    if(m_mapIdToUserItem[rq->m_id]){
        UserItem *temp = nullptr;
        temp = m_mapIdToUserItem[rq->m_id];
        m_MainWnd->removeGroupItem(m_mapIdToUserItem[rq->m_id]);
        m_mapIdToUserItem.remove(rq->m_id);
        delete temp;
        temp = nullptr;
    }
}
//处理群聊好友列表信息
void CKernel::dealGroupListInfo(long lSendIP, char *buf, int nLen)
{
    qDebug() << __func__;
    //拆包
    STRU_GROUP_LIST_INFO* info = (STRU_GROUP_LIST_INFO*)buf;
    //根据groupId找到对应的groupDialog然后将用户名字设定上
    m_mapIdToGroupdialog[info->groupId]->setUserInfo(info->userName);
}

